/*
    This is a set of libraries to assist in OpenGl and Other Applications
    Copyright (C) 2008  The Modular Engine Team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#define HUDMODULE
#include "HUDTextbox.h"

namespace ModularEngine
{

	HUDTextbox::HUDTextbox()
	{
		// Start up textbox

		// Character Width
		mCharWidth = 9;
		// Start w/ no text
		msText = "";
		// Start Cursor at 0
		mCursor = 0;
		// No scroll originally.
		mxScroll = 0;

		// Initialize original color scheme.
		LOADCOLORS;

		mLines = 0;

		mbMultiline = false;
		mbInput = true;

		bMemory = false;

		mtFunc = 0;
	}

	HUDTextbox::~HUDTextbox()
	{

	}

	int HUDTextbox::initialize()
	{
		msTextRecall.push_back( "" );
		nRecallNumber = 0;
		if( mbMultiline )
		{

			mScrollbar = new HUDScrollbar();
			mScrollbar->mvDimensions = Vector2d( 15, mvDimensions.y-1 );
			mScrollbar->mvPosition = mvPosition + Vector2d( mvDimensions.x-14, 0 );
			//mScrollbar->setContentHeight( 1000 );
			mvOriginalPosition = mvPosition;

			// Calc scrollbar position


		}

		return 1;
	}

	void HUDTextbox::draw()
	{
		if( mbMultiline )
			drawMultiline();
		else
			drawSingleline();
	}

	void HUDTextbox::clearTextBox()
	{
		msText = "";
	}

#define CHARACTERHEIGHT 13
	
	using namespace std;

	void HUDTextbox::addText( const char *text )
	{
		msText += '\n';
		msText += text;
		mLines++;

		mScrollbar->setContentHeight( (countLines( msText, mvDimensions.x-15, 9 ) + 2 )* CHARACTERHEIGHT );
		mScrollbar->setScroll( (countLines( msText, mvDimensions.x-15, 9 ) + 2 ) * CHARACTERHEIGHT - mvDimensions.y );

	}

	int HUDTextbox::findLastSpace( int nWidth, std::string str )
	{
		for( int j = 0; j < nWidth && j < str.length(); j++ )
		{
			if( str[j] == '\n' )
				return j+1;
		}

		int i = nWidth;

		if( str.length() < nWidth )
			return str.length();
		
		if( i > str.length() )
			i = str.length();

		while( i > 0 )
		{
			if( str[i] == ' ' )
			{
				i++;
				break;			
			}
			i--;
		}

		if( i == 0 )
			i = nWidth;

		if( i > str.length() )
			i = str.length();
		return i;
	}


	using namespace std;

	int HUDTextbox::countLines( std::string str, int nWidth, int nCharWidth )
	{
		// Counts the number of lines based off of str.

		// lPL = letters per line
		int lPL = nWidth/nCharWidth;
		int line = 0;
		int lastReturn = 0;
		while(1)
		{
			int newLine = lastReturn;
			if( lastReturn + lPL > str.length() )
			{
				for( int i = 0; i < str.length()-lastReturn; i++ )
				{
					if( str[i+lastReturn] == '\n' )
					{
						lastReturn += i;
						break;
					}
				}
				
				if( newLine == lastReturn && str[lastReturn] != '\n' )
					break;
			}

			for( int i = 0; i < lPL; i++ )
			{
				if( str[i+lastReturn] == '\n' )
				{
					lastReturn += i;
					break;
				} else if( str[i+lastReturn] == ' ' )
				{
					newLine = i+lastReturn;
				}
			}

			if( newLine > lastReturn )
				lastReturn = newLine;

			++lastReturn;
			++line;
		}

		return line;
		
	}

	void HUDTextbox::drawMultilineText()
	{
		gl->setCoords( mvPosition );

		glClearStencil( 0 );
		glClear( GL_STENCIL_BUFFER_BIT );
		glEnable( GL_STENCIL_TEST );
		glStencilFunc( GL_NEVER, 0x0, 0x0 );
		glStencilOp( GL_INCR, GL_INCR, GL_INCR );
		glColor4f( 1, 1, 1, 1 );

		gl->drawRect( Vector2d( 0, 0 ), mvDimensions-Vector2d( 17, 0 ) );

		glStencilFunc( GL_LEQUAL, 0x1, 0x1 );
		glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
		
		glColor3f( mvTextColor.x, mvTextColor.y, mvTextColor.z );
	
		int i = 0;	

		std::string str = msText + "\n";
		
		int lPL = (mvDimensions.x-15)/9;
		int line = 0;
		int lastReturn = 0;
		int start = 0;
		while(1)
		{
			int newLine = lastReturn;
			if( lastReturn + lPL > str.length() )
			{
				for( int i = 0; i < str.length()-lastReturn; i++ )
				{
					if( str[i+lastReturn] == '\n' )
					{
						lastReturn += i+1;
						break;
					}
				}
				if( newLine == lastReturn )
					break;
			}
			if( newLine == lastReturn )
			{
				for( int i = 0; i < lPL; i++ )
				{
					if( str[i+lastReturn] == '\n' )
					{
						lastReturn += i+1;
						break;
					} else if( str[i+lastReturn] == ' ' )
					{
						newLine = i+lastReturn+1;
					}
				}
			}

			if( newLine > lastReturn )
				lastReturn = newLine;
		
			if( mScrollbar->getScroll() < line * CHARACTERHEIGHT+CHARACTERHEIGHT*2 && mScrollbar->getScroll()+mvDimensions.y > line * CHARACTERHEIGHT )
				gl->drawText( (char *)msText.substr( start, lastReturn-start ).c_str(), Vector2d( 0, line*CHARACTERHEIGHT-mScrollbar->getScroll() ) );
			++line;

			start = lastReturn;
		}


		

		mLines = line;

		mScrollbar->setContentHeight( (line+1) * CHARACTERHEIGHT );
		
		glDisable( GL_STENCIL_TEST );	

	}

	void HUDTextbox::drawMultiline()
	{

		gl->setCoords( mvPosition );

		Vector2d posDiff = mvPosition - mvOriginalPosition;
		mvOriginalPosition = mvPosition;
		mScrollbar->mvPosition = mScrollbar->mvPosition + posDiff;

		glColor4f( 0, 0, 0, .8 );

		gl->drawRect( Vector2d( -1, -1 ), Vector2d(mvDimensions.x + 2, mvDimensions.y+2) );

		gl->drawGradientRect( Vector2d( 0, 0 ), mvDimensions, mvBaseColor, mvBaseColor - .2 );


		int original = mScrollbar->getScroll();
	
		mScrollbar->draw();

		gl->setCoords( mvPosition );

		drawMultilineText();







	}

	void HUDTextbox::drawSingleline()
	{

		// Get the mouse point
		Vector2d cPoint = ModularEngine::WindowModule::getSingleton().getMouse();

		// Does the parent have focus?
		bool pFocus = true;

		if( pParent )
		{
			pFocus = pParent == HUDModule::getSingleton().mpHoveredElement;
		}

		// If the cursor is in the textbox, draw an IBEAM
		if( gl->pointInRect( cPoint, mvPosition, mvDimensions ) && pFocus )
		{
			ModularEngine::WindowModule::getSingleton().setMouse( IDC_IBEAM );
			SetCursor( LoadCursor( NULL, IDC_IBEAM ) );

		}

		gl->setCoords( mvPosition );

		// Draw the textbox and border
		glColor4f( mvTextColor.x, mvTextColor.y, mvTextColor.z, 1 );
		gl->drawRect( Vector2d( 0, 0 ), mvDimensions );
		gl->drawGradientRect( Vector2d( 1, 1 ), mvDimensions-Vector2d( 2, 2 ), mvBaseColor, mvBaseColor*.7 );


		// Clip around the box so text doesn't go oustide of it.
		glClearStencil( 0 );
		glClear( GL_STENCIL_BUFFER_BIT );
		glEnable( GL_STENCIL_TEST );
		glStencilFunc( GL_NEVER, 0x0, 0x0 );
		glStencilOp( GL_INCR, GL_INCR, GL_INCR );
		glColor4f( 1, 1, 1, 1 );
		gl->drawRect( Vector2d( 3, 0 ), mvDimensions-Vector2d( 6, 0 ) );

		glStencilFunc( GL_LEQUAL, 0x1, 0x1 );
		glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );

		glColor3f( mvTextColor.x, mvTextColor.y, mvTextColor.z );

		// Number of characters behind the textbox
		int nHiddenChars = mxScroll/mCharWidth;

		// Draw text that can be seen... eh
		// TODO: Don't draw characters that are past the text box.

		// BUG: If the x-position is too close to the edge of the screen, 
		// i.e. <= 7, The text doesn't draw after it scrolls

		gl->drawText( (char *)msText.substr( nHiddenChars, (msText.length()-nHiddenChars) ).c_str(), Vector2d( 3-mxScroll+mCharWidth*nHiddenChars, ((mvDimensions.y-10)/2)-6) );

		// Draw Cursor
		// Have it blink over some small interval
		bool focus = false;
		if( pParent )
		{
			if( this == pParent->mpFocusedElement && pParent == HUDModule::getSingleton().mpFocusedElement )
				focus = true;
		} else {
			if( this == ModularEngine::HUDModule::getSingleton().mpFocusedElement )
				focus = true;
		}
		
		if( focus )
		{
			if( (mCursorTimer.timeElapsed()/400)%2 == 1 )
			{
				glBegin( GL_LINES );
					glVertex2f( mCursor*mCharWidth+3-mxScroll, 4 );
					glVertex2f( mCursor*mCharWidth+3-mxScroll, mvDimensions.y-5 );
				glEnd();
			}
		}

		glDisable( GL_STENCIL_TEST );


	}
	
	void HUDTextbox::mouseInput( Vector2d vPos, UINT nDown )
	{

		// Left button down.  Calculate where we clicked.
		if( nDown == 0 )
		{
			if( !mbMultiline )
			{
				Vector2d nPoint = vPos-mvPosition;
				nPoint.x += mxScroll;
				mCursor = nPoint.x/mCharWidth;
				if( mCursor > msText.length() )
					mCursor = msText.length();
			} else
			{
				if( vPos.x > mvDimensions.x - 17 )
				{
					mScrollbar->mouseInput( vPos, nDown );
				}
			}
		}
	}

	void HUDTextbox::keyboardInput( UINT nKey )
	{

		// Arbitrary numbers for left and right
		// delete = 0
		// left = 12
		// right = 13
		// home = 1
		// end = 2
		// enter/return = 3
		// up = 4



		if( mbInput )
		{

			unsigned char mKey = nKey;
			unsigned int l = (unsigned char)mKey;

			if( mKey != 4 && mKey != 5 )
				nRecallNumber = msTextRecall.size()-1;

			switch( mKey )
			{
			case 1:
				mCursor = 0;
				break;
			case 2:
				mCursor = msText.length();
				break;
			case 3:

				if( mtFunc )
				{
					if( bMemory )
					{
						msTextRecall.push_back( msText );
						nRecallNumber = msTextRecall.size()-1;
					}

					//cout << msText << endl;
					//cout << msText.length() << endl;

					(*mtFunc)();
				}
				break;
			case 4:				
				if( bMemory )
				{
					nRecallNumber++;
					if( nRecallNumber > msTextRecall.size()-1 )
						nRecallNumber = 0;

					setText( (char *)msTextRecall[msTextRecall.size()-nRecallNumber-1].c_str() );
				}
				break;
			case 5:
				if( bMemory )
				{
					nRecallNumber--;
					if( nRecallNumber < 0 )
						nRecallNumber = msTextRecall.size()-1;
					setText( (char *)msTextRecall[msTextRecall.size()-nRecallNumber-1].c_str() );
				}
				break;
			case 0:
				{

					if( mCursor < msText.length() )
					{
						// copy everything up to the (cursor)
						// Skip the character directly in front of the cursor
						// Copy the rest.
						std::string temp = msText.substr( 0, msText.length()-(msText.length()-mCursor) );
						temp += msText.substr( msText.length()-(msText.length()-mCursor)+1, msText.length() );
						msText = temp;
					}

				} break;
			case 8:
				{
					// If the cursor is set at 0 we can't delete nothing.
					if( mCursor > 0 )
					{
						// copy everything up to the (cursor-1)
						// Skip the character directly behind the cursor
						// Copy the rest.
						std::string temp = msText.substr( 0, msText.length()-(msText.length()-mCursor)-1 );
						temp += msText.substr( msText.length()-(msText.length()-mCursor), msText.length() );
						msText = temp;
					}

					// Cursor can't be < 0.
					--mCursor;
					if( mCursor < 0 )
						mCursor = 0;
				
				} break;
			case 12:
				{
					// Cursor can't be < 0
					--mCursor;
					if( mCursor < 0 )
						mCursor = 0;
				} break;
			case 13:
				{
					// Cursor can't be in front of what's not written.
					++mCursor;
					if( mCursor > msText.length() )
						mCursor = msText.length();
				} break;

			}


			if( mKey >= 32 )
			{
				std::string temp = msText.substr( 0, msText.length()-(msText.length()-mCursor) );
				temp += mKey;
				temp += msText.substr( msText.length()-(msText.length()-mCursor), msText.length() );

				msText = temp;
				++mCursor;
			}

			adjustScroll();
		}
	}

	void HUDTextbox::adjustScroll()
	{

		// If the cursor is past the window and scroll factor, adjust
		if( mCursor*mCharWidth > mxScroll + mvDimensions.x - mCharWidth*2 )
			mxScroll = mCursor*mCharWidth - mvDimensions.x + mCharWidth*2;
		// If Cursor is behind window and scroll factor, adjust
		if( mCursor*mCharWidth < mxScroll + mCharWidth*2 )
			mxScroll = mCursor*mCharWidth - mCharWidth*2;

		// mxScroll can't be less than 0 or greater than the text.
		if( mxScroll < 0 )
			mxScroll = 0;
		if( mxScroll > mCursor*mCharWidth+mvDimensions.x )
			mxScroll = mCursor*mCharWidth+mvDimensions.x;
	}
}
